using System.ComponentModel.DataAnnotations;
using System.Reflection;
using System.Text.RegularExpressions;
using System.Windows.Forms;
using SqlSugar;
using Sunny.UI;
using 数据迁移工具.Common;
using 数据迁移工具.Common.DbContext;
using 数据迁移工具.Factory;

namespace 数据迁移工具
{
    public partial class Form1 : UIForm
    {
        bool srcConnState = false;
        bool dstConnState = false;

        string srcConfigId = string.Empty;
        string dstConfigId = string.Empty;

        SugarDbContext sourceDbContext;
        SugarDbContext dstDbContext;

        List<ConnectionConfig> ConnectionConfigs = new List<ConnectionConfig>();
        List<string> selectedTables = new List<string>();

        List<string> dbTypes = new List<string>()
        {
            "Dm",
            "MySql",
            "SqlServer",
            "Sqlite",
            "Oracle"
        };

        public string SrcConnStr
        {
            get
            {
                string sql = "";
                DbType type = Enum.Parse<DbType>(SrcCbBox.SelectedValue.ToString());
                switch (type)
                {
                    case DbType.MySql:
                        sql = $"Server={SrcIPTxt.Text.Trim()};Port={SrcPortTxt.Text.Trim()};Database={SrcDbTxt.Text.Trim()};user={SrcNameTxt.Text.Trim()};Password={SrcPwdTxt.Text.Trim()};AllowLoadLocalInfile=true";
                        break;
                    case DbType.SqlServer:
                        throw new Exception("未实现针对SqlServer的转换");
                        break;
                    case DbType.Sqlite:
                        sql = $"Data Source={SrcIPTxt.Text.Trim()}";
                        break;
                    case DbType.Dm:
                        sql = $"Server={SrcIPTxt.Text.Trim()};Port={SrcPortTxt.Text.Trim()};Database={SrcDbTxt.Text.Trim()};user={SrcNameTxt.Text.Trim()};Password={SrcPwdTxt.Text.Trim()}";
                        break;
                    case DbType.Oracle:
                        sql = $"Data Source=(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP)(HOST={SrcIPTxt.Text.Trim()})(PORT={SrcPortTxt.Text.Trim()})))(CONNECT_DATA=(SERVICE_NAME=xe)));User Id={SrcNameTxt.Text.Trim()};Password={SrcPwdTxt.Text.Trim()};";
                        break;
                    default:
                        break;
                }

                return sql;
            }
        }

        public string DstConnStr
        {
            get
            {
                string sql = "";
                DbType type = Enum.Parse<DbType>(DstCbBox.SelectedValue.ToString());
                switch (type)
                {
                    case DbType.MySql:
                        sql = $"Server={DstIPTxt.Text.Trim()};Port={DstPortTxt.Text.Trim()};Database={DstDbTxt.Text.Trim()};user={DstNameTxt.Text.Trim()};Password={DstPwdTxt.Text.Trim()};AllowLoadLocalInfile=true";
                        break;
                    case DbType.SqlServer:
                        throw new Exception("未实现针对SqlServer的转换");
                        break;
                    case DbType.Sqlite:
                        sql = $"Data Source={SrcIPTxt.Text.Trim()}";
                        break;
                    case DbType.Dm:
                        sql = $"Server={DstIPTxt.Text.Trim()};Port={DstPortTxt.Text.Trim()};Database={DstDbTxt.Text.Trim()};user={DstNameTxt.Text.Trim()};Password={DstPwdTxt.Text.Trim()}";
                        break;
                    case DbType.Oracle:
                        sql = $"Data Source=(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP)(HOST={DstIPTxt.Text.Trim()})(PORT={DstPortTxt.Text.Trim()})))(CONNECT_DATA=(SERVICE_NAME={DstDbTxt.Text.Trim()})));User Id={DstNameTxt.Text.Trim()};Password={DstPwdTxt.Text.Trim()};";
                        break;
                    default:
                        break;
                }

                return sql;
            }
        }

        public string SrcDbType { get; set; }
        public string DstDbType { get; set; }

        public Form1()
        {
            InitializeComponent();
            Load += Form1_Load;
        }

        private void Form1_Load(object? sender, EventArgs e)
        {
            SrcCbBox.DataSource = dbTypes;
            DstCbBox.DataSource = dbTypes;

            ConsoleLog.AddControl("RichText", LogTextBox);
            ConsoleLog.AddControl("statusStrip", statusStrip1);
        }

        /// <summary>
        /// 根据传入配置信息构建ConnectionConfig
        /// </summary>
        /// <param name="origin"></param>
        /// <param name="dbType"></param>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>
        public static ConnectionConfig BuildConfig(Origin origin, string dbType, string configId, string SrcConnStr, string DstConnStr)
        {
            ConnectionConfig connConfig = new ConnectionConfig()
            {
                ConfigId = configId,
                InitKeyType = InitKeyType.Attribute,
                IsAutoCloseConnection = true,
                ConfigureExternalServices = new ConfigureExternalServices
                {
                    //注意:  这儿AOP设置不能少
                    EntityService = (c, p) =>
                    {
                        if (p.IsPrimarykey)
                        {
                            p.IsNullable = false;
                        }
                        else if (c.PropertyType.IsGenericType && c.PropertyType.GetGenericTypeDefinition() == typeof(Nullable<>))
                        {
                            p.IsNullable = true;
                        }
                        else if (c.PropertyType == typeof(string))
                        {
                            if (c.GetCustomAttribute<RequiredAttribute>() == null)
                                p.IsNullable = true;
                        }
                    }
                }
            };

            DbType type = Enum.Parse<DbType>(dbType);
            switch (type)
            {
                case DbType.MySql:
                    connConfig.DbType = DbType.MySql;
                    break;
                case DbType.SqlServer:
                    throw new Exception("未实现针对SqlServer的转换");
                case DbType.Sqlite:
                    connConfig.DbType = DbType.Sqlite;
                    break;
                case DbType.Dm:
                    connConfig.DbType = DbType.Dm;
                    break;
                case DbType.Oracle:
                    connConfig.DbType = DbType.Oracle;
                    break;
                default:
                    break;
            }
            connConfig.ConnectionString = origin == Origin.Source ? SrcConnStr : DstConnStr;
            return connConfig;
        }

        /// <summary>
        /// 连接源数据
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void SrcConnBtn_Click(object sender, EventArgs e)
        {
            string dbType = SrcCbBox.SelectedValue.ToString();
            string srcIp = SrcIPTxt.Text;
            string srcPort = SrcPortTxt.Text;
            string dbName = SrcDbTxt.Text;
            string username = SrcNameTxt.Text;
            string password = SrcPwdTxt.Text;

            CheckParam(Origin.Source, dbType, srcIp, srcPort, dbName, username, password);
            srcConfigId = dbType + DateTime.Now.ToString("yyyyMMddHHmmssfff");
            ConnectionConfig srcConnConfig = BuildConfig(Origin.Source, dbType, srcConfigId, SrcConnStr, DstConnStr);

            sourceDbContext = DbFactory.CreateDbContext(Enum.Parse<DbType>(dbType), srcConnConfig);
            bool connState = sourceDbContext.DB.Ado.IsValidConnection();

            srcConnState = connState;
            if (connState)
            {
                SrcConnLabel.Text = "已连接";
                SrcConnLabel.ForeColor = Color.Black;
            }
            else
            {
                SrcConnLabel.Text = "未连接";
                SrcConnLabel.ForeColor = Color.Red;
            }

            try
            {
                AllSelectBtn.Checked = false;
                AllCancelBtn.Checked = false;
                CkListBox.Items.Clear();

                List<TableInfo> tableInfos = sourceDbContext.GetTableInfos(dbName);
                CkListBox.Items.AddRange(tableInfos.Select(t => t.TbName).ToArray());
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }

        public static bool CheckParam(Origin source, string dbType, string srcIp, string srcPort, string dbName, string username, string password)
        {
            if (source == Origin.Source)
            {
                if (string.IsNullOrEmpty(dbType))
                {
                    MessageBox.Show("数据源类型不能为空");
                    return false;
                }
                if (string.IsNullOrEmpty(srcIp))
                {
                    MessageBox.Show("数据源 'IP' 不能为空");
                    return false;
                }
                if (string.IsNullOrEmpty(srcPort) && !"Sqlite".Equals(dbType))
                {
                    MessageBox.Show("数据源 '端口' 不能为空");
                    return false;
                }
                if (string.IsNullOrEmpty(dbName) && !"Sqlite".Equals(dbType))
                {
                    MessageBox.Show("数据源 '数据库名' 不能为空");
                    return false;
                }
            }
            else if (source == Origin.Destination)
            {
                if (string.IsNullOrEmpty(dbType))
                {
                    MessageBox.Show("目标源类型不能为空");
                    return false;
                }
                if (string.IsNullOrEmpty(srcIp))
                {
                    MessageBox.Show("目标源 'IP' 不能为空");
                    return false;
                }
                if (string.IsNullOrEmpty(srcPort) && !"Sqlite".Equals(dbType))
                {
                    MessageBox.Show("目标源 '端口' 不能为空");
                    return false;
                }
                if (string.IsNullOrEmpty(dbName) && !"Sqlite".Equals(dbType))
                {
                    MessageBox.Show("目标源 '数据库名' 不能为空");
                    return false;
                }
            }
            return true;
        }

        /// <summary>
        /// 连接目标数据库
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void DstConnBtn_Click(object sender, EventArgs e)
        {
            string dbType = DstCbBox.SelectedValue.ToString();
            string dstIp = DstIPTxt.Text;
            string dstPort = DstPortTxt.Text;
            string dbName = DstDbTxt.Text;
            string username = DstNameTxt.Text;
            string password = DstPwdTxt.Text;

            CheckParam(Origin.Source, dbType, dstIp, dstPort, dbName, username, password);
            dstConfigId = dbType + DateTime.Now.ToString("yyyyMMddHHmmssfff");
            ConnectionConfig dstConnConfig = BuildConfig(Origin.Destination, dbType, dstConfigId, SrcConnStr, DstConnStr);
            dstDbContext = DbFactory.CreateDbContext(Enum.Parse<DbType>(dbType), dstConnConfig);
            bool connState = dstDbContext.DB.Ado.IsValidConnection();
            dstConnState = connState;

            if (connState)
            {
                DstConnLabel.Text = "已连接";
                DstConnLabel.ForeColor = Color.Black;
            }
            else
            {
                DstConnLabel.Text = "未连接";
                DstConnLabel.ForeColor = Color.Red;
            }
        }

        /// <summary>
        /// 执行数据迁移
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private async void MigrateBtn_ClickAsync(object sender, EventArgs e)
        {
            if (srcConnState && dstConnState)
            {
                ProcTbNameLb.Visible = true;
                CurTbNameLb.Visible = true;
                Probar.Visible = true;
                ProLb.Visible = true;

                LogTextBox.Clear();
                MigrateBtn.Enabled = false;
                foreach (Control item in uiGroupBox1.Controls)
                {
                    if(item is not UILabel)
                    {
                        item.Enabled = false;
                    }
                }
                foreach (Control item in uiGroupBox2.Controls)
                {
                    if (item is not UILabel)
                    {
                        item.Enabled = false;
                    }
                }

                await Task.Run(() =>
                {
                    BeginMigrate();
                });
                MigrateBtn.Enabled = true;
                foreach (Control item in uiGroupBox1.Controls)
                {
                    if (item is not UILabel)
                    {
                        item.Enabled = true;
                    }
                }
                foreach (Control item in uiGroupBox2.Controls)
                {
                    if (item is not UILabel)
                    {
                        item.Enabled = true;
                    }
                }
            }
            else
            {
                MessageBox.Show("请检查数据源和目标源连接状况", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        /// <summary>
        /// 初始化连接配置列表
        /// </summary>
        private void InitConfigs()
        {
            this.Invoke(() =>
            {
                ConnectionConfigs.Clear();
                ConnectionConfigs.Add(BuildConfig(Origin.Source, SrcDbType, srcConfigId, SrcConnStr, DstConnStr));
                ConnectionConfigs.Add(BuildConfig(Origin.Destination, DstDbType, dstConfigId, SrcConnStr, DstConnStr));
            });
            SugarDbContext.ConnectionConfigs = ConnectionConfigs;
        }

        private void BeginMigrate()
        {
            InitConfigs();
            ResetStatusStrip();
            string modelPath = Directory.GetCurrentDirectory() + "\\tmp";
            if (!Directory.Exists(modelPath))
                Directory.CreateDirectory(modelPath);
            string assemblyName = $"Default.Models{DateTime.Now.ToString("yyyyMMddHHmmssfff")}";
            try
            {
                sourceDbContext.GenerateTmpModels(srcConfigId, modelPath, selectedTables, namesp: assemblyName,
                processLog: str => { ConsoleLog.Info("RichText", str); });
                DynamicHelper.AssemblyBuild(modelPath, assemblyName);
                dstDbContext.OpenAopLog();
                dstDbContext.GenerateTables(dstConfigId);

                if (SrcDbType == "Dm" && DstDbType == "MySql")
                {
                    sourceDbContext.DbMigrate(DbType.MySql, dstConfigId, SrcDbTxt.Text, assemblyName);
                }
                else if (SrcDbType == "Oracle" && DstDbType == "MySql")
                {
                    sourceDbContext.DbMigrate(DbType.MySql, dstConfigId, SrcDbTxt.Text, assemblyName);
                }
                else
                {
                    ConsoleLog.Warn("RichText", "该迁移方式尚未实现");
                }
            }
            finally
            {
                Directory.Delete(modelPath, true);
            }
        }

        private void AllSelectBtn_CheckedChanged(object sender, EventArgs e)
        {
            if (AllSelectBtn.Checked)
            {
                AllCancelBtn.Checked = false;

                for (int i = 0; i < CkListBox.Items.Count; i++)
                {
                    CkListBox.SetItemChecked(i, true);
                }
                selectedTables.AddRange(CkListBox.Items.Cast<string>().ToList());
            }
        }

        private void AllCancelBtn_CheckedChanged(object sender, EventArgs e)
        {
            if (AllCancelBtn.Checked)
            {
                AllSelectBtn.Checked = false;

                for (int i = 0; i < CkListBox.Items.Count; i++)
                {
                    CkListBox.SetItemChecked(i, false);
                }
                selectedTables.Clear();
            }

        }

        private void CkListBox_SelectedIndexChanged(object sender, EventArgs e)
        {
            selectedTables.Clear();
            selectedTables.AddRange(CkListBox.CheckedItems.Cast<string>().ToList());
        }

        int lastSrcSelectedIndex = 0;
        private void SrcCbBox_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (srcConnState && lastSrcSelectedIndex != SrcCbBox.SelectedIndex)
            {
                DialogResult res = MessageBox.Show("当前状态下切换类型会导致连接断开", "提示", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
                if (res == DialogResult.Yes)
                {
                    sourceDbContext = null;
                    CkListBox.Items.Clear();
                    SrcConnLabel.ForeColor = Color.DarkRed;
                    SrcConnLabel.Text = "未连接";
                }
                else
                {
                    return;
                }
            }

            SrcDbType = SrcCbBox.SelectedValue.ToString();
            SrcIPTxt.ResetText();
            SrcPortTxt.ResetText();
            SrcDbTxt.ResetText();
            SrcNameTxt.ResetText();
            SrcPwdTxt.ResetText();


        }

        int lastDstSelectedIndex = 0;
        private void DstCbBox_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (dstConnState && lastDstSelectedIndex != DstCbBox.SelectedIndex)
            {
                DialogResult res = MessageBox.Show("当前状态下切换类型会导致连接断开", "提示", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
                if (res == DialogResult.Yes)
                {
                    dstDbContext = null;
                    CkListBox.Items.Clear();
                    DstConnLabel.ForeColor = Color.DarkRed;
                    DstConnLabel.Text = "未连接";
                }
                else
                {
                    return;
                }
            }

            DstDbType = DstCbBox.SelectedValue.ToString();
            DstIPTxt.ResetText();
            DstPortTxt.ResetText();
            DstNameTxt.ResetText();
            DstPwdTxt.ResetText();
        }

        /// <summary>
        /// 保存日志
        /// </summary>
        string initDirPath = Application.CommonAppDataPath;
        private async void uiButton2_ClickAsync(object sender, EventArgs e)
        {
            SaveFileDialog fileDialog = new SaveFileDialog();
            fileDialog.InitialDirectory = initDirPath;
            fileDialog.Filter = "文本文件|*.txt|日志文件|*.log";
            DialogResult result = fileDialog.ShowDialog();
            if (result == DialogResult.OK)
            {
                string savePath = fileDialog.FileName;
                initDirPath = Directory.GetParent(savePath).FullName;

                await File.AppendAllTextAsync(savePath, LogTextBox.Text, System.Text.Encoding.UTF8);
            }
        }

        private void uiButton1_Click(object sender, EventArgs e)
        {
            LogTextBox.Clear();
        }

        /// <summary>
        /// 展示实体类生成页面
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void uiButton5_Click(object sender, EventArgs e)
        {
            ModelForm form = new ModelForm();
            form.Tag = dbTypes;
            form.ShowDialog();
        }

        /// <summary>
        /// 重置 StatusStrip 状态
        /// </summary>
        private void ResetStatusStrip()
        {
            statusStrip1.Invoke(() =>
            {
                statusStrip1.Items["CurTbNameLb"].Text = "";
                ToolStripProgressBar progressBar = (ToolStripProgressBar)statusStrip1.Items["Probar"];
                progressBar.Value = 0;
                statusStrip1.Items["ProLb"].Text = "";
            });
        }

        /// <summary>
        /// 迁移视图
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void uiButton4_Click(object sender, EventArgs e)
        {
            if (srcConnState && dstConnState)
            {
                InitConfigs();
                ProcTbNameLb.Visible = true;
                CurTbNameLb.Visible = true;
                Probar.Visible = true;
                ProLb.Visible = true;
                ResetStatusStrip();

                if (Enum.TryParse<DbType>(DstDbType, out DbType dbType))
                {
                    sourceDbContext.ViewConvert(SrcNameTxt.Text.Trim(), dbType, dstDbContext.ConfigId, DstDbTxt.Text.ToString());
                }
            }
            else
            {
                MessageBox.Show("请检查数据源和目标源连接状况", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }
    }

    public enum Origin
    {
        Source,
        Destination
    }
}
